﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Net;
using AMIUtilities;


//    Copyright (C) 2011 Officebroker.com LTD.
//    Neil Cadman, Alex Welding, Andrew Coombs, Jan Rygielski
//    Sir Robert Peel Mill, Mill Lane, Staffordshire, Fazely, B78 3QD, England
//
//    This file is part of AMIProxy Project.
//
//    AMIProxy Project is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    AMIProxy Project is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License (GNU AGPL)
//    along with AMIProxy Project.  If not, see <http://www.gnu.org/licenses/>.


namespace AMICallStatLogic
{
    public class CallStatLogic
    {
        // Private Variables
        private TcpClient _tcpClient;
        private ManagedSocket _managedSocket;
        private CallStatAddedDelegate _callStatAdded;
        private NewMessageDelegate _newMessage;
        private ManagedSocket.socketClosedDelegate _socketClosed;
        private ManagedSocket.errorDelegate _socketException;

        private String command = "";

        private const String ENDLINEORNOTHING = "(|$)";
        private const String ANYTHINGANDSPACES = "([ \\S ]*)";
        private const String ACTIONPATTERN = "(Action: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String EXTENSIONPATTERN = "(Extension: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String STARTDATEPATTERN = "(StartDateTimeUTC: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String ENDDATEPATTERN = "(EndDateTimeUTC: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String ANSWEREDDATEPATTERN = "(AnsweredDateTimeUTC: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String NUMBERPATTERN = "(Number: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String DIRECTIONPATTERN = "(Direction: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String INTERNALPATTERN = "(Internal: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String TRANSFERREDFROMPATTERN = "(TransferedFrom: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;
        private const String TRANSFERREDTYPEPATTERN = "(TransferType: )" + ANYTHINGANDSPACES + ENDLINEORNOTHING;

        // Public Properties
        public event ManagedSocket.socketClosedDelegate SocketClosed
        {
            add { _socketClosed += value; }
            remove { _socketClosed -= value; }
        }
        public event ManagedSocket.errorDelegate SocketException
        {
            add { _socketException += value; }
            remove { _socketException -= value; }
        }

        // Private Delegates 
        private delegate void readSocketDelegate(Socket socket);

        // Public Delegates
        public delegate void CallStatAddedDelegate(CallStat callStat);
        public delegate void NewMessageDelegate(String message);

        // Public Events
        public event CallStatAddedDelegate CallStatAdded
        {
            add { _callStatAdded += value; }
            remove { _callStatAdded -= value; }
        }
        public event NewMessageDelegate NewMessage
        {
            add { _newMessage += value; }
            remove { _newMessage -= value; }
        }

        // Public Methods
        public void InitiateConnection(String hostName, Int32 port)
        {
            _tcpClient = new TcpClient(hostName, port);

            _managedSocket = new ManagedSocket(RandomFactory.Create().Next(1000000).ToString(), _tcpClient.Client);
            _managedSocket.DataReceived += new ManagedSocket.dataReceivedDelegate(_managedSocket_DataReceived);
            _managedSocket.SocketClosed += new ManagedSocket.socketClosedDelegate(_managedSocket_SocketClosed);
            _managedSocket.Error += new ManagedSocket.errorDelegate(_managedSocket_SocketException);
            _managedSocket.ReadStart();
        }

        public void ShutDown()
        {
            if (_managedSocket != null)
                _managedSocket.Close();

            if (_tcpClient != null)
                _tcpClient.Close();
        }
        public void SendMessageToAMI(String message)
        {
            _managedSocket.SendMessage(message);
        }

        // Private Methods
        private void HandleMessage(String message)
        {

            Match actionMatch;

            actionMatch = Regex.Match(message, ACTIONPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline);

            if (actionMatch.Success)
                if (actionMatch.Result("$2") == "ChannelLog")
                    NewCallStat(message);

            OnNewMessage(message);

        }
        private void NewCallStat(String message)
        {
            String extension;
            DateTime startDateTimeUTC;
            DateTime endDateTimeUTC;
            DateTime answeredDateTimeUTC;
            String number;
            String direction;
            Boolean callInternal;
            String transferredFrom;
            String transferType;

            extension = Regex.Match(message, EXTENSIONPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2");
            startDateTimeUTC = DateTime.Parse(Regex.Match(message, STARTDATEPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2"));
            endDateTimeUTC = DateTime.Parse(Regex.Match(message, ENDDATEPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2"));
            answeredDateTimeUTC = DateTime.Parse(Regex.Match(message, ANSWEREDDATEPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2"));
            number = Regex.Match(message, NUMBERPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2");
            direction = Regex.Match(message, DIRECTIONPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2");
            callInternal = Boolean.Parse(Regex.Match(message, INTERNALPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2"));
            transferredFrom = Regex.Match(message, TRANSFERREDFROMPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2");
            transferType = Regex.Match(message, TRANSFERREDTYPEPATTERN, RegexOptions.IgnoreCase & RegexOptions.Multiline).Result("$2");

            OnCallStatAdded(new CallStat(extension, startDateTimeUTC, endDateTimeUTC, answeredDateTimeUTC, number, direction, callInternal, transferredFrom, transferType));
        }
        private void _managedSocket_DataReceived(ManagedSocket socket, Byte[] data)
        {
            String[] lines;

            command += Encoding.ASCII.GetString(data, 0, data.Length);

            lines = Regex.Split(command, "\\r\\n\\r\\n", RegexOptions.Multiline);

            for (Int32 i = 0; i < lines.Length - 1; i++)
            {
                HandleMessage(lines[i]);
            }

            command = lines[lines.Length - 1];
        }
        private void _managedSocket_SocketClosed(ManagedSocket managedSocket)
        {
            OnSocketClosed();
        }
        private void _managedSocket_SocketException(String message, Exception e)
        {
            OnSocketException(e);
        }

        private void OnSocketClosed()
        {
            if (_socketClosed != null)
                _socketClosed.RaiseEventSafely(new Object[] { });
        }
        private void OnSocketException(Exception e)
        {
            if (_socketException != null)
                _socketException.RaiseEventSafely(new Object[] { e });
        }
        private void OnCallStatAdded(CallStat callStat)
        {
            if (_callStatAdded != null)
                _callStatAdded.RaiseEventSafely(new Object[] { callStat });
        }
        private void OnNewMessage(String message)
        {
            if (_newMessage != null)
                _newMessage.RaiseEventSafely(new Object[] { message });
        }

        // Public Structure
        public struct CallStat
        {
            // Private Variables
            private String _extension;
            private DateTime _startDateTimeUTC;
            private DateTime _endDateTimeUTC;
            private DateTime _answeredDateTimeUTC;
            private String _number;
            private String _direction;
            private Boolean _internal;
            private String _transferredFrom;
            private String _transferType;

            // Public Variables
            public String Extension
            {
                get { return _extension; }
            }
            public DateTime StartDateTimeUTC
            {
                get { return _startDateTimeUTC; }
            }
            public DateTime EndDateTimeUTC
            {
                get { return _endDateTimeUTC; }
            }
            public DateTime AnsweredDateTimeUTC
            {
                get { return _answeredDateTimeUTC; }
            }
            public String Number
            {
                get { return _number; }
            }
            public String Direction
            {
                get { return _direction; }
            }
            public Boolean Internal
            {
                get { return _internal; }
            }
            public String TransferredFrom
            {
                get { return _transferredFrom; }
            }
            public String TransferType
            {
                get { return _transferType; }
            }

            // Public Constructor
            public CallStat(String extension, DateTime startDateTimeUTC, DateTime endDateTimeUTC, DateTime answeredDateTimeUTC, String number, String direction, Boolean callInternal, String transferredFrom, String transferType)
            {
                _extension = extension;
                _startDateTimeUTC = startDateTimeUTC;
                _endDateTimeUTC = endDateTimeUTC;
                _answeredDateTimeUTC = answeredDateTimeUTC;
                _number = number;
                _direction = direction;
                _internal = callInternal;
                _transferredFrom = transferredFrom;
                _transferType = transferType;
            }
        }
    }
}
